package com.zbest.zpay.acctrans.component;

import java.util.Date;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.zbest.common.lang.base.Money;
import com.zbest.common.lang.util.CoreDateUtils;
import com.zbest.zpay.acctrans.common.exception.AccTransRuntimeException;
import com.zbest.zpay.acctrans.common.util.BalanceTagUtils;
import com.zbest.zpay.acctrans.dal.mapper.AccountBalanceMapper;
import com.zbest.zpay.acctrans.dal.mapper.AccountDetailMapper;
import com.zbest.zpay.acctrans.dal.model.AccountBalance;
import com.zbest.zpay.acctrans.dal.model.AccountDetail;
import com.zbest.zpay.acctrans.dal.model.AccountInf;
import com.zbest.zpay.acctrans.dal.model.TransJnl;
import com.zbest.zpay.acctrans.enums.CapStsFlg;
import com.zbest.zpay.acctrans.enums.DebitCreditHoldFlag;
import com.zbest.zpay.acctrans.enums.UpdBalFlg;
import com.zbest.zpay.acctrans.enums.biz.AcctransBizCode;

public class MainAccountDepositComponent {
    private static final Logger logger = LoggerFactory
            .getLogger(MainAccountDepositComponent.class);

    private Money zero = new Money(0, Money.DEFAULT_CURRENCY);

    @Autowired
    private AccountBalanceMapper accountBalanceMapper;

    @Autowired
    private AccountDetailMapper accountDetailMapper;

    @Autowired
    private CreateMainAccountBalanceComponent createMainAccountBalanceComponent;

    public void execute(TransJnl transJnl, AccountInf accountInf) {
        logger.info("[MAIN ACCOUNT DEPOSIT!]");
        if (transJnl == null) {
            logger.info("AccountTransJnl不能为空");
            throw new AccTransRuntimeException(AcctransBizCode.ACT_FIELD_NOT_NULL, "transJnl不能为空");
        }
        String jrnNo = transJnl.getJrnNo();
        String actNo = transJnl.getCrActNo();
        String usrId = transJnl.getCrUsrId();
        String actTyp = transJnl.getCrActTyp();
        String actDat = transJnl.getActDat();

        String updBalFlg = accountInf.getUpdBalFlg();
        if (!UpdBalFlg.isDefineByCode(updBalFlg)) {
            logger.info("[UpdBalFlg标志不合法]");
            throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_UPDBAL_FLAG,
                    "[UpdBalFlg标志不合法]");
        }
        logger.info("[判断主账户是否日终批量更新余额:]" + updBalFlg);
        if (StringUtils.equals(updBalFlg, UpdBalFlg.NOT_UPDATE.getCode())) {
            /** 登记上一笔交易的余额信息 */
            // 日终批量更新，解决热点问题
            writeMainActDrCrDetailBat(transJnl);
            return;
        }

        logger.info("[查询主账户余额信息]");
        AccountBalance mainAccountBalance = null;
        /** 在主账户余额信息表中判断是否存在该账户的余额信息,如果存在再 for update该记录，避免死锁 */
        long retNum = accountBalanceMapper.qryCntByActNo(actNo);
        if (retNum == 1) {
            mainAccountBalance = accountBalanceMapper.qryByActNoForUpd(actNo);
        }
        if (mainAccountBalance == null) {
            // 创建主账户余额信息
            createMainAccountBalanceComponent.execute(actTyp, usrId,
                    actNo, actDat, jrnNo);
            mainAccountBalance = accountBalanceMapper.qryByActNoForUpd(actNo);
        } else {
            long curActBal = mainAccountBalance.getCurActBal();
            Money curbal = new Money(curActBal, Money.DEFAULT_CURRENCY);
            boolean tagCheckRet = BalanceTagUtils.verifyBalanceTag(actNo, curbal,
                    mainAccountBalance.getBalTag());
            if (!(tagCheckRet)) {
                throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_CURBAL_ILLEGAL,
                        "账户余额金额值非法");
            }
        }

        // 更新主账户余额信息
        updMainActBal(transJnl, mainAccountBalance);
        writeMainActDrCrDetail(transJnl, accountInf, mainAccountBalance);
        logger.info("[主账户存入成功]");
    }

    public void updMainActBal(TransJnl transJnl, AccountBalance mainAccountBalance) {
        logger.info("[更新主账户余额信息]");
        Money lastBalAmt = new Money(mainAccountBalance.getLastActBal(), Money.DEFAULT_CURRENCY);
        String lastActDat = mainAccountBalance.getUpdDat();
        Money curActBal = new Money(mainAccountBalance.getCurActBal(), Money.DEFAULT_CURRENCY);
        String actPostDt = transJnl.getActDat();
        Money crAmt = new Money(transJnl.getFctTxAmt(), Money.DEFAULT_CURRENCY);
        Money lastUavaBal = new Money(mainAccountBalance.getLastUavaBal(), Money.DEFAULT_CURRENCY);
        Money notTxAvaBal = new Money(mainAccountBalance.getNotTxAvaBal(), Money.DEFAULT_CURRENCY);
        Money acBal = curActBal.add(crAmt);
        if ((crAmt.compareTo(zero) < 0) && (acBal.compareTo(zero) < 0)) {
            throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_TXAMT_ILLEGAL, "交易金额非法");
        }

        if (Long.valueOf(actPostDt) > Long.valueOf(lastActDat)) {
            lastBalAmt = new Money(mainAccountBalance.getCurActBal(), Money.DEFAULT_CURRENCY);
            lastUavaBal = new Money(mainAccountBalance.getUavaBal(), Money.DEFAULT_CURRENCY);
            lastActDat = actPostDt;
        } else if (Long.valueOf(actPostDt) < Long.valueOf(lastActDat)) {
            lastBalAmt = lastBalAmt.add(crAmt);
        }

        String balTag = BalanceTagUtils.createBalanceTag(mainAccountBalance.getActNo(), acBal);
        mainAccountBalance.setCurActBal(acBal.getCent());
        mainAccountBalance.setLastActBal(lastBalAmt.getCent());
        mainAccountBalance.setLastUavaBal(lastUavaBal.getCent());
        mainAccountBalance.setBalTag(balTag);
        mainAccountBalance.setUpdDat(lastActDat);
        mainAccountBalance.setUpdJrn(transJnl.getJrnNo());
        mainAccountBalance.setNotTxAvaBal(notTxAvaBal.getCent());
        mainAccountBalance.setActNo(mainAccountBalance.getActNo());
        int retCd = accountBalanceMapper.updateBalByActNo(mainAccountBalance);
        if (retCd == 0) {
            throw new AccTransRuntimeException(AcctransBizCode.ACT_ACCOUNT_UPDBAL_FAIL, "更新主账户余额失败");
        }
    }

    public void writeMainActDrCrDetail(TransJnl transJnl, AccountInf accountInf, AccountBalance mainAccountBalance) {
        /** 登记上一笔交易的余额信息 */
        long lastActBal = mainAccountBalance.getLastActBal();
        long lastUavaBal = mainAccountBalance.getLastUavaBal();
        Money objLastActBal = new Money(lastActBal, Money.DEFAULT_CURRENCY);
        Money objLastUavaBal = new Money(lastUavaBal, Money.DEFAULT_CURRENCY);
        Money objLastAvaBal = objLastActBal.subtract(objLastUavaBal);

        AccountDetail mainAccountDetail = packAccountDetail(transJnl);

        mainAccountDetail.setBal(mainAccountBalance.getCurActBal());
        mainAccountDetail.setNotTxAmt(mainAccountBalance.getNotTxAvaBal());
        mainAccountDetail.setUavaBal(mainAccountBalance.getUavaBal());
        mainAccountDetail.setLastAvaBal(objLastAvaBal.getCent());

        mainAccountDetail.setMainCdSts(CapStsFlg.CAP_STATUS_NORMAL.getCode());
        mainAccountDetail.setUpdBalFlg(UpdBalFlg.IS_UPDATE.getCode());
        accountDetailMapper.insert(mainAccountDetail);
    }

    public void writeMainActDrCrDetailBat(TransJnl transJnl) {
        AccountDetail mainAccountDetail = packAccountDetail(transJnl);
        mainAccountDetail.setMainCdSts(CapStsFlg.CAP_STATUS_NOT.getCode());
        mainAccountDetail.setUpdBalFlg(UpdBalFlg.NOT_UPDATE.getCode());
        accountDetailMapper.insert(mainAccountDetail);
    }

    public AccountDetail packAccountDetail(TransJnl transJnl) {
        logger.info("[WRITE  MAIN ACCOUNT DETAIL RECORD!]");
        AccountDetail mainAccountDetail = new AccountDetail();
        mainAccountDetail.setSysDat(CoreDateUtils.yyyymmdd(new Date()));
        mainAccountDetail.setSysTim(CoreDateUtils.hh24miss(new Date()));
        mainAccountDetail.setJrnNo(transJnl.getJrnNo());
        mainAccountDetail.setJrnSeq(transJnl.getJrnSeq());
        mainAccountDetail.setActNo(transJnl.getCrActNo());
        mainAccountDetail.setActTyp(transJnl.getCrActTyp());
        mainAccountDetail.setOldTxDt(transJnl.getOldTxDt());
        mainAccountDetail.setOldTxTm(transJnl.getOldTxTm());
        mainAccountDetail.setActDat(transJnl.getActDat());
        mainAccountDetail.setTxCd(transJnl.getTxCd());
        mainAccountDetail.setBusTyp(transJnl.getBusTyp());
    //    mainAccountDetail.setTxOppTyp(transJnl.getDrActTyp());
        mainAccountDetail.setTxOppCd(transJnl.getDrUsrId());
        mainAccountDetail.setOppAc(transJnl.getDrActNo());
        mainAccountDetail.setDcFlg(DebitCreditHoldFlag.CREDIT_FLAG.getCode());
        mainAccountDetail.setCrAmt(transJnl.getFctTxAmt());
        mainAccountDetail.setTxDesc(transJnl.getTxDesc());
        mainAccountDetail.setOrdTyp(transJnl.getOrdTyp());
        mainAccountDetail.setOrdNo(transJnl.getOrdNo());
        mainAccountDetail.setUsrId(transJnl.getCrUsrId());
        mainAccountDetail.setTxAmt(transJnl.getOrdAmt());
        mainAccountDetail.setFeeAmt(transJnl.getFeeAmt());
        mainAccountDetail.setMercId(transJnl.getMercId());
        mainAccountDetail.setVchNoTyp(transJnl.getVchNoTyp());
        mainAccountDetail.setVchNo(transJnl.getVchNo());
        mainAccountDetail.setRmk(transJnl.getTxDesc());
        mainAccountDetail.setBusCnl(transJnl.getBusCnl());
        mainAccountDetail.setSysCnl(transJnl.getSysCnl());
        mainAccountDetail.setHldAmt(transJnl.getHldAmt());
        mainAccountDetail.setHldNo(transJnl.getHldNo());
        return mainAccountDetail;
    }
}